package com.cheer.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ZipUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cheer.dao.*;
import com.cheer.dto.*;
import com.cheer.entity.*;
import com.cheer.enums.*;
import com.cheer.exception.ServiceException;
import com.cheer.manager.StandingBookReviewManager;
import com.cheer.properties.DocumentProperties;
import com.cheer.service.ReferenceService;
import com.cheer.service.StandingBookService;
import com.cheer.vo.DocumentNumberVO;
import com.cheer.vo.StandingBookVO;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author cheer
 */
@Service
@Slf4j
public class StandingBookServiceImpl extends ServiceImpl<StandingBookDao, StandingBook> implements StandingBookService {

    @Autowired
    private DocumentProperties documentProperties;

    @Autowired
    private AccountDao accountDao;

    @Autowired
    private ApplicantDao applicantDao;

    @Autowired
    private OrganizationDao organizationDao;

    @Autowired
    private StandingBookDao standingBookDao;

    @Autowired
    private StandingBookDocumentDao standingBookDocumentDao;

    @Autowired
    private StandingBookReviewDao standingBookReviewDao;

    @Autowired
    private StandingBookReviewManager standingBookReviewManager;

    @Autowired
    private StandingBookReviewLogDao standingBookReviewLogDao;

    @Override
    public StandingBookVO getByAccountId(Long accountId) {
        // 查询账户、集体、机构、台账信息
        Account account = accountDao.selectById(accountId);
        if (account == null || account.getLocked()) {
            throw new ServiceException("账号已失效，无法查看台账");
        }
        Applicant applicant = applicantDao.getByAccountId(accountId);
        if (applicant == null) {
            throw new ServiceException("账号下不存在有效的集体信息，无法查看台账");
        }
        Organization organization = organizationDao.selectById(applicant.getOrgId());
        if (organization == null) {
            throw new ServiceException("集体所在的组织机构已失效，无法查看台账");
        }
        StandingBook standingBook = standingBookDao.getByAccountId(accountId);
        List<StandingBookDocument> documents = standingBookDocumentDao.listByAccountId(accountId);
        Organization joinOrg = null;
        if (applicant.getReviewType() == ReviewTypeEnum.JOIN.getCode()) {
            joinOrg = organizationDao.selectById(applicant.getJoinReviewOrgId());
            if (joinOrg == null) {
                throw new ServiceException("联合审核机构不存在或已失效");
            }
        }
        // 组装返回数据
        return StandingBookVO.build(account, applicant, organization, joinOrg, standingBook, documents,
                documentProperties.getUrl());
    }

    @Override
    @Transactional
    public void saveStandingBook(Long accountId, StandingBookSaveDTO standingBookSaveDTO) {
        // 验证保存条件
        if (standingBookSaveDTO.getYouthNumber() > standingBookSaveDTO.getPersonNumber()) {
            throw new ServiceException("青年人数不能大于职工人数");
        }
        if (standingBookSaveDTO.getYouthNumber() * 2 < standingBookSaveDTO.getPersonNumber()) {
            throw new ServiceException("青年人数比例不能低于 50%");
        }
        if(DateUtil.compare(standingBookSaveDTO.getContactPersonBirth(), new Date()) > 0){
            throw new ServiceException("出生日期必须小于当前日期");
        }
        if (DateUtil.ageOfNow(standingBookSaveDTO.getContactPersonBirth()) >= 40) {
            throw new ServiceException("号长年龄必须小于 40 岁");
        }
        Applicant applicant = checkApplicant(accountId);
        StandingBook standingBook = checkStandingBook(accountId);
        // 保存集体、台账信息
        updateApplicant(applicant.getId(), standingBookSaveDTO);
        saveStandingBook(accountId, standingBookSaveDTO, standingBook);
    }

    @Override
    public List<DocumentNumberVO> getDocument(Long accountId) {
        List<StandingBookDocument> documents = standingBookDocumentDao.listByAccountId(accountId);
        return documents.stream()
                .filter(document -> document.getDocType() != DocTypeEnum.MEMBER.getCode() &&
                        document.getDocType() != DocTypeEnum.FILE.getCode())
                .map(document -> DocumentNumberVO.build(document, documentProperties.getUrl()))
                .collect(Collectors.toList());
    }

    @Override
    @Transactional
    public void saveDocument(Long accountId, List<DocumentDTO> documentDTOS) {
        // 验证保存条件
        checkApplicant(accountId);
        checkStandingBook(accountId);
        // 保存台账文档
        standingBookDocumentDao.deleteByAccountIdAndBetweenType(accountId, DocTypeEnum.DOC_MIN.getCode(),
                DocTypeEnum.DOC_MAX.getCode());
        List<StandingBookDocument> documents = documentDTOS.stream()
                .map(documentDTO -> documentDTO.build(accountId))
                .collect(Collectors.toList());
        standingBookDocumentDao.insertBatch(documents);

        Applicant applicant = applicantDao.getByAccountId(accountId);
        // 查询如果当前集体状态是创建状态则插入
        if(applicant.getRegisterStatus().equals((RegisterStatusEnum.PROVINCE.getCode()))){
            StandingBookReviewLog log = new StandingBookReviewLog();
            log.setReviewType(RegisterStatusEnum.PROVINCE.getCode());
            log.setApplicantId(applicant.getId());
            log.setAction("提交");
            log.setCause("提交省级创建台账资料");
            standingBookReviewLogDao.insert(log);
        }

    }

    @Override
    public String downloadDocument(Long accountId) {
        // 查询需要下载的文件
        List<StandingBookDocument> documents = standingBookDocumentDao.listByAccountId(accountId);
        documents = documents.stream()
                .filter(document -> document.getDocType() != DocTypeEnum.MEMBER.getCode() &&
                        document.getDocType() != DocTypeEnum.FILE.getCode())
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(documents)) {
            throw new ServiceException("当前没有已上传的台账资料");
        }
        // 压缩文件
        String[] fileNames = documents.stream()
                .map(document -> (document.getDocType() - 2) + File.separator +
                        StringUtils.substringAfterLast(document.getDocUrl(), File.separator))
                .toArray(String[]::new);
        InputStream[] filePaths = documents.stream()
                .map(document -> {
                    try {
                        return new FileInputStream(documentProperties.getPath() + document.getDocUrl());
                    } catch (FileNotFoundException e) {
                        log.warn("文件未找到：" + e.getMessage(), e);
                    }
                    return null;
                })
                .filter(Objects::nonNull)
                .toArray(InputStream[]::new);
        String zipPath = File.separator + accountId + File.separator + "台账资料 " + DateUtil.formatDateTime(new Date())
                + ".zip";
        ZipUtil.zip(new File(documentProperties.getPath() + zipPath), fileNames, filePaths);
        return documentProperties.getUrl() + zipPath;
    }

    @Override
    @Transactional
    public void submitStandingBook(Long accountId, StandingBookSubmitDTO standingBookSubmitDTO) {
        // 验证提交条件
        Applicant applicant = applicantDao.getByAccountId(accountId);
        if (applicant == null) {
            throw new ServiceException("账号下不存在有效的集体信息，无法提交文档");
        }
        StandingBook standingBook = standingBookDao.getByAccountId(accountId);
        if (standingBook == null) {
            throw new ServiceException("台账信息不存在，无法提交台账");
        }
        if (standingBook.getReviewStatus() == ReviewedStatusEnum.REVIEWING.getCode()) {
            throw new ServiceException("台账已在审核中，无法重复提交");
        }
        // 修改台账状态
        StandingBook update = new StandingBook();
        update.setId(standingBook.getId());
        update.setReviewStatus(ReviewedStatusEnum.REVIEWING.getCode());
        standingBookDao.updateById(update);
        // 启动审核流程
        standingBookReviewManager.submitReview(accountId, applicant.getOrgId());
        if (applicant.getReviewType() == ReviewTypeEnum.JOIN.getCode()) {
            standingBookReviewManager.submitReview(accountId, applicant.getJoinReviewOrgId());
        }
    }

    @Override
    public StandingBookVO getByApplicantId(Long applicantId) {
        Applicant applicant = applicantDao.selectById(applicantId);
        if (applicant == null) {
            throw new ServiceException("集体已失效，无法查看台账");
        }
        return getByAccountId(applicant.getAccountId());
    }

    @Override
    public List<DocumentNumberVO> getDocumentByApplicantId(Long applicantId) {
        Applicant applicant = applicantDao.selectById(applicantId);
        if (applicant == null) {
            throw new ServiceException("集体已失效，无法查看台账");
        }
        return getDocument(applicant.getAccountId());
    }

    @Override
    public String downloadDocumentByApplicantId(Long applicantId) {
        Applicant applicant = applicantDao.selectById(applicantId);
        if (applicant == null) {
            throw new ServiceException("集体已失效，无法下载台账文档");
        }
        return downloadDocument(applicant.getAccountId());
    }

    @Override
    public StandingBookVO getByReviewId(Long reviewId) {
        StandingBookReview review = standingBookReviewDao.selectById(reviewId);
        if (review == null) {
            throw new ServiceException("审核已失效，无法查看台账");
        }
        StandingBookVO standingBookVO = getByAccountId(review.getAccountId());
        // 更改审核状态
        standingBookVO.setReviewStatus(review.getReviewStatus());
        return standingBookVO;
    }

    @Override
    public List<DocumentNumberVO> getDocumentByReviewId(Long reviewId) {
        StandingBookReview review = standingBookReviewDao.selectById(reviewId);
        if (review == null) {
            throw new ServiceException("审核已失效，无法查看台账");
        }
        return getDocument(review.getAccountId());
    }

    @Override
    @Transactional
    public void saveStandingBookByReview(StandingBookSaveInReviewDTO standingBookSaveInReviewDTO) {
        // 验证保存条件
        StandingBookReview review = checkReview(standingBookSaveInReviewDTO.getReviewId());
        Applicant applicant = checkApplicant(review.getAccountId());
        StandingBook standingBook = standingBookDao.getByAccountId(review.getAccountId());
        // 保存集体、台账信息
        updateApplicant(applicant.getId(), standingBookSaveInReviewDTO);
        saveStandingBook(review.getAccountId(), standingBookSaveInReviewDTO, standingBook);
    }

    @Override
    @Transactional
    public void saveDocumentByReview(StandingBookDocumentSaveDTO standingBookDocumentSaveDTO) {
        // 验证保存条件
        StandingBookReview review = checkReview(standingBookDocumentSaveDTO.getReviewId());
        checkApplicant(review.getAccountId());
        // 保存台账文档
        standingBookDocumentDao.deleteByAccountIdAndBetweenType(review.getAccountId(), DocTypeEnum.DOC_MIN.getCode(),
                DocTypeEnum.DOC_MAX.getCode());
        if (CollectionUtils.isNotEmpty(standingBookDocumentSaveDTO.getDocuments())) {
            List<StandingBookDocument> documents = standingBookDocumentSaveDTO.getDocuments().stream()
                    .map(documentDTO -> documentDTO.build(review.getAccountId()))
                    .collect(Collectors.toList());
            standingBookDocumentDao.insertBatch(documents);
        }
    }

    @Override
    public String downloadDocumentByReviewId(Long reviewId) {
        StandingBookReview review = standingBookReviewDao.selectById(reviewId);
        if (review == null) {
            throw new ServiceException("审核已失效，无法下载台账文档");
        }
        return downloadDocument(review.getAccountId());
    }

    @Override
    public Boolean apply(Long accountId) {
        final Applicant applicant = applicantDao.getByAccountId(accountId);
        if (applicant == null) {
            throw new ServiceException("账号下不存在有效的集体信息");
        }
        // 查看当前账号是否有台账文件
        StandingBookDocument jobDocument = standingBookDocumentDao.listByAccountIdAndTypes(accountId, DocTypeEnum.jobInfo);
        if(jobDocument == null){
            throw new ServiceException("请上传文明号岗位基本情况");
        }
        StandingBookDocument memberDocument = standingBookDocumentDao.listByAccountIdAndTypes(accountId, DocTypeEnum.member);
        if(memberDocument == null){
            throw new ServiceException("");
        }

        String yyyy = DateUtil.format(new Date(), "yyyy");
        if(yyyy.equals(applicant.getVintage()) &&
                applicant.getRegisterStatus().equals(RegisterStatusEnum.PROVINCE_TITLE.getCode())){
            throw new ServiceException("本年度已授予完成请明年再次申请！");
        }
        if (applicant.getRegisterStatus() < RegisterStatusEnum.CITY_TITLE.getCode()) {
            return false;
        }
        // 省级文明
        if (applicant.getRegisterStatus() == RegisterStatusEnum.CITY_TITLE.getCode() ||
                applicant.getRegisterStatus() == RegisterStatusEnum.PROVINCE_TITLE.getCode() ||
                (yyyy.equals(applicant.getVintage()) == false)) {
            Applicant updateApplicant = new Applicant();
            updateApplicant.setId(applicant.getId());
            updateApplicant.setRegisterStatus(RegisterStatusEnum.PROVINCE.getCode());
            updateApplicant.setVintage(DateUtil.format(new Date(),"yyyy"));
            applicantDao.updateById(updateApplicant);
        }
        return true;
    }

    /**
     * 验证集体是否可以保存台账
     *
     * @param accountId 账户Id
     */
    private Applicant checkApplicant(Long accountId) {
        Applicant applicant = applicantDao.getByAccountId(accountId);
        if (applicant == null) {
            throw new ServiceException("账号下不存在有效的集体信息，无法保存台账");
        }
        return applicant;
    }

    /**
     * 验证台账是否可以修改
     *
     * @param accountId 账户Id
     */
    private StandingBook checkStandingBook(Long accountId) {
        StandingBook standingBook = standingBookDao.getByAccountId(accountId);
        if (standingBook != null && standingBook.getReviewStatus() == ReviewedStatusEnum.REVIEWING.getCode()) {
            throw new ServiceException("台账在审核中，无法修改台账");
        }
        return standingBook;
    }

    /**
     * 修改集体内容
     *
     * @param applicantId         集体Id
     * @param standingBookSaveDTO 台账提交信息
     */
    private void updateApplicant(Long applicantId, StandingBookSaveDTO standingBookSaveDTO) {
        Organization joinOrg = null;
        if (standingBookSaveDTO.getReviewType() == ReviewTypeEnum.JOIN.getCode()) {
            joinOrg = organizationDao.selectById(standingBookSaveDTO.getJoinReviewOrgId());
            if (joinOrg == null) {
                throw new ServiceException("联合审核机构不存在或已失效");
            }
        }
        Applicant applicant = standingBookSaveDTO.buildApplicant(applicantId, joinOrg);
        applicantDao.updateById(applicant);
    }

    /**
     * 保存台账信息、台账文档
     *
     * @param accountId           账户Id
     * @param standingBookSaveDTO 台账提交信息
     * @param standingBook        保存过的台账
     */
    private void saveStandingBook(Long accountId, StandingBookSaveDTO standingBookSaveDTO, StandingBook standingBook) {
        // 新增或更新台账信息
        if (standingBook == null) {
            standingBook = standingBookSaveDTO.buildStandingBook(accountId);
            standingBookDao.insert(standingBook);
        } else {
            StandingBook updateStandingBook = standingBookSaveDTO.buildUpdateStandingBook(standingBook.getId());
            standingBookDao.updateById(updateStandingBook);
        }
        // 保存台账文档
        standingBookDocumentDao.deleteByAccountIdAndInType(accountId, Lists.newArrayList(DocTypeEnum.MEMBER.getCode(),
                DocTypeEnum.FILE.getCode()));
        List<StandingBookDocument> documents = standingBookSaveDTO.buildDocuments(accountId);
        standingBookDocumentDao.insertBatch(documents);
    }

    /**
     * 验证审核是否有效
     *
     * @param reviewId 审核Id
     */
    private StandingBookReview checkReview(Long reviewId) {
        StandingBookReview review = standingBookReviewDao.selectById(reviewId);
        if (review == null) {
            throw new ServiceException("审核已失效，无法保存台账");
        }
        if (review.getReviewStatus() == ReviewStatusEnum.PASSED.getCode() ||
                review.getReviewStatus() == ReviewStatusEnum.NOT_PASSED.getCode() ||
                review.getReviewStatus() == ReviewStatusEnum.REJECTED.getCode()) {
            throw new ServiceException("您当前无权限修改该审核的台账");
        }
        return review;
    }

    /**
     * 补丁补充文件记录
     */
    @Override
    public void patch() {

        List<StandingBookDocument> standingBookReviews = standingBookDocumentDao.selectList(new QueryWrapper<>());
        Map<Long, List<StandingBookDocument>> accountMap = standingBookReviews.stream().collect(Collectors.groupingBy(StandingBookDocument::getAccountId));

        List<StandingBookDocument> batch = new ArrayList<>();
        for (Long accountId : accountMap.keySet()) {
            List<StandingBookDocument> standingBookDocuments = accountMap.get(accountId);
            Optional<StandingBookDocument> firstDoc = standingBookDocuments.stream().filter(doc -> doc.getDocType().equals(1)).findFirst();
            Optional<StandingBookDocument> fiveDoc = standingBookDocuments.stream().filter(doc -> doc.getDocType().equals(5)).findFirst();
            // 五号文件不存在
            if(fiveDoc.isPresent() == false){
                // 一号文件存在
                if(firstDoc.isPresent() == true){
                    StandingBookDocument standingBookDocument = firstDoc.get();
                    standingBookDocument.setId(null);
                    standingBookDocument.setDocType(5);
                    batch.add(standingBookDocument);
                }
            }
        }
        if(CollectionUtils.isNotEmpty(batch)){
            standingBookDocumentDao.insertBatch(batch);
        }
    }

}
